package com.yanfan.ba.util;

import com.alibaba.fastjson2.JSONObject;
import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.RemoteDevice;
import com.serotonin.bacnet4j.exception.BACnetException;
import com.serotonin.bacnet4j.exception.PropertyValueException;
import com.serotonin.bacnet4j.npdu.ip.IpNetwork;
import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder;
import com.serotonin.bacnet4j.transport.DefaultTransport;
import com.serotonin.bacnet4j.type.enumerated.ObjectType;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier;
import com.serotonin.bacnet4j.util.PropertyReferences;
import com.serotonin.bacnet4j.util.PropertyValues;
import com.serotonin.bacnet4j.util.ReadListener;
import com.serotonin.bacnet4j.util.RequestUtils;

import java.util.ArrayList;
import java.util.List;

public class BacnetDataRetriever {

    public static JSONObject retrieveData() {
        JSONObject jsonObject = new JSONObject();
        LocalDevice localDevice = null;
        try {
            // 创建网络对象
            IpNetwork ipNetwork = new IpNetworkBuilder()
                    .withLocalBindAddress("10.22.10.241")
                    .withSubnet("255.255.255.0", 24)
                    .withPort(47808)
                    .withReuseAddress(true)
                    .build();

            // 创建虚拟的本地设备并初始化
            localDevice = new LocalDevice(223, new DefaultTransport(ipNetwork));
            localDevice.initialize();
            localDevice.startRemoteDeviceDiscovery();

            // 获取远程设备
            Integer remoteDeviceId = 222;
            RemoteDevice remoteDevice = localDevice.getRemoteDeviceBlocking(remoteDeviceId);

            // 获取远程设备的标识符对象
            List<ObjectIdentifier> objectList = RequestUtils.getObjectList(localDevice, remoteDevice).getValues();

            List<ObjectIdentifier> analogInputObjectIdentifierList = new ArrayList<>();
            List<ObjectIdentifier> binaryInputObjectIdentifierList = new ArrayList<>();

            // 过滤对象标识符列表
            for (ObjectIdentifier oi : objectList) {
                if (oi.getObjectType().equals(ObjectType.analogInput)) {
                    analogInputObjectIdentifierList.add(new ObjectIdentifier(ObjectType.analogInput, oi.getInstanceNumber()));
                }
                if (oi.getObjectType().equals(ObjectType.binaryInput)) {
                    binaryInputObjectIdentifierList.add(new ObjectIdentifier(ObjectType.binaryInput, oi.getInstanceNumber()));
                }
            }

            // 读取属性值
            PropertyValues pvAnalogInputObjectName = readValueByPropertyIdentifier(localDevice, remoteDevice, analogInputObjectIdentifierList, null, PropertyIdentifier.objectName);
            PropertyValues pvAnalogInputPresentValue = readValueByPropertyIdentifier(localDevice, remoteDevice, analogInputObjectIdentifierList, null, PropertyIdentifier.presentValue);
            PropertyValues pvAnalogInputDescription = readValueByPropertyIdentifier(localDevice, remoteDevice, analogInputObjectIdentifierList, null, PropertyIdentifier.description);

            PropertyValues pvBinaryInputObjectName = readValueByPropertyIdentifier(localDevice, remoteDevice, binaryInputObjectIdentifierList, null, PropertyIdentifier.objectName);
            PropertyValues pvBinaryInputPresentValue = readValueByPropertyIdentifier(localDevice, remoteDevice, binaryInputObjectIdentifierList, null, PropertyIdentifier.presentValue);
            PropertyValues pvBinaryInputDescription = readValueByPropertyIdentifier(localDevice, remoteDevice, binaryInputObjectIdentifierList, null, PropertyIdentifier.description);

            List<JSONObject> analogInputDataList = buildJsonObjectList(analogInputObjectIdentifierList, pvAnalogInputObjectName, pvAnalogInputPresentValue, pvAnalogInputDescription);
            jsonObject.put("analogInputData", analogInputDataList);

            List<JSONObject> binaryInputDataList = buildJsonObjectList(binaryInputObjectIdentifierList, pvBinaryInputObjectName, pvBinaryInputPresentValue, pvBinaryInputDescription);
            jsonObject.put("binaryInputData", binaryInputDataList);

//            System.out.println(jsonObject.toString());

        } catch (BACnetException | PropertyValueException e) {
            // 更细致地处理BACnet相关异常，提供更有意义的错误信息
            System.err.println("在获取BACnet数据时出现异常: " + e.getMessage());
            // 可以根据具体情况进行更多的错误处理操作，比如记录日志等
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 确保无论是否出现异常，都能关闭本地设备，释放资源
            if (localDevice!= null) {
                localDevice.terminate();
            }
        }

        return jsonObject;
    }

    private static List<JSONObject> buildJsonObjectList(List<ObjectIdentifier> objectIdentifierList,
                                                        PropertyValues objectNamePropertyValues,
                                                        PropertyValues presentValuePropertyValues,
                                                        PropertyValues descriptionPropertyValues) throws PropertyValueException {
        List<JSONObject> jsonObjectList = new ArrayList<>();
        for (ObjectIdentifier oi : objectIdentifierList) {
            JSONObject jsonData = new JSONObject();
            jsonData.put("objectType", oi.getObjectType().toString());
            jsonData.put("instanceNumber", oi.getInstanceNumber());
            jsonData.put("name", objectNamePropertyValues.get(oi, PropertyIdentifier.objectName).toString());
            jsonData.put("presentValue", presentValuePropertyValues.get(oi, PropertyIdentifier.presentValue).toString());
            jsonData.put("description", descriptionPropertyValues.get(oi, PropertyIdentifier.description).toString());
            jsonObjectList.add(jsonData);
        }
        return jsonObjectList;
    }

    public static PropertyValues readValueByPropertyIdentifier(final LocalDevice localDevice, final RemoteDevice d,
                                                               final List<ObjectIdentifier> ois, final ReadListener callback, PropertyIdentifier propertyIdentifier) throws BACnetException {
        if (ois.size() == 0) {
            return new PropertyValues();
        }

        // 先进行一些前置条件检查，如果不满足提前返回或抛出更具体的异常
        if (localDevice == null || d == null) {
            throw new IllegalArgumentException("本地设备或远程设备为空");
        }

        final PropertyReferences refs = new PropertyReferences();
        for (final ObjectIdentifier oid : ois) {
            refs.add(oid, propertyIdentifier);
        }

        try {
            return RequestUtils.readProperties(localDevice, d, refs, false, callback);
        } catch (BACnetException e) {
            // 这里可以根据具体情况进一步处理异常，比如记录日志等
            System.err.println("在读取属性值时出现异常: " + e.getMessage());
            throw e;
        }
    }
}
